﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Foxit.PDF;
using Foxit.PDF.Imaging;
using Foxit.PDF.PageElements;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.Reporting.Framework;

namespace Sustainalytics.ReportGenerator.BL
{
    public class ReportParametersSection : IPdfDocumentGenerator
    {
        private CompanyReportDataOffline _dataSource;
        private PdfReport _report;
        public const float EmptyRowHeight = 10f;

        # region Static Images

        private static byte[] _trendArrowDown;
        private static byte[] _trendArrowUp;
        private static byte[] _trendArrowRight;
        public byte[] TrendArrowDown { get { return _trendArrowDown; } }
        public byte[] TrendArrowUp { get { return _trendArrowUp; } }
        public byte[] TrendArrowRight { get { return _trendArrowRight; } }


        private static byte[] _glossaryOfTermsDistributionChart;
        public byte[] GlossaryOfTermsDistributionChart { get { return _glossaryOfTermsDistributionChart; } }


        static void LoadStaticImages()
        {
            if (_trendArrowDown == null)
                _trendArrowDown = ImagesGenerator.GetJpgImage(@"Images\trend_arrow_down.jpg");

            if (_trendArrowUp == null)
                _trendArrowUp = ImagesGenerator.GetJpgImage(@"Images\trend_arrow_up.jpg");

            if (_trendArrowRight == null)
                _trendArrowRight = ImagesGenerator.GetJpgImage(@"Images\trend_arrow_right.jpg");


            
            
            if (_glossaryOfTermsDistributionChart == null)
                _glossaryOfTermsDistributionChart = ImagesGenerator.GetJpgImage(@"Images\GlossaryOfTermsDistributionChart.jpg");
        }

        #endregion

        static ReportParametersSection()
        {
            LoadStaticImages();
        }

        public GenerationResult GeneratePdfDocument(CompanyReportDataOffline crdo)
        {
            var result = new GenerationResult
            {
                ReportType = crdo.ReportType,
                ReportGeneratorType = ReportGeneratorType.ParametersSection,
                OutlinePositions = new List<OutlinePosition>(),
                PageElementPositions = new List<PageElementPosition>()
            };
            var sw = Stopwatch.StartNew();

            _dataSource = crdo;

            _report = new PdfReport();

            ComposeReportParametersSection();

            if (_report.Pages.Any())
                result.PdfDocument = _report.Draw();

            sw.Stop();
            result.GenerationTime = sw.Elapsed;

            return result;
        }

        private void ComposeReportParametersSection()
        {
            const float startPositionForDisclaimer = 470;
            float top = 0;

            var page = _report.NewPage();
            _report.Pages.Add(page);



            if (_dataSource.ReportType == ReportType.ComprehensiveReport ||
                _dataSource.ReportType == ReportType.SummaryComprehensiveReport)
            {
                var parametersTable = AddReportParameters();

                page.AddElement(parametersTable);

                top = parametersTable.GetRequiredHeight() + 15f;

                top = AddGlossaryOfTermsForCompanyReport(page, top);
            }

            if (_dataSource.ReportType == ReportType.CoreReport || 
                _dataSource.ReportType == ReportType.SummaryCoreReport)
                top = AddGlossaryOfTermsForFocusedRatings(page, top);

            if (_dataSource.ReportType == ReportType.ControversyReport)
                top = AddGlossaryOfTermsForControversyReport(page, top);


            if (top > startPositionForDisclaimer)
            {
                page = _report.NewPage();
                _report.Pages.Add(page);
            }

            AddDisclaimer(_report.Pages.ElementAt(_report.Pages.Count - 1), startPositionForDisclaimer);
        }
        


        private Table AddReportParameters()
        {         
            const float firstColumnWidth = 200f;
            const float paddingLeft = -4;

            var container = BuildSectionTable("Report Parameters");

            var settingsSectionHeading = container.Rows.Add();

            AddSubsectionCell(settingsSectionHeading, "Settings", paddingLeft);

            var settingsSection = container.Rows.Add();
            settingsSection.Cells.Add(new FormattedTextArea("<p>The relative performance data in this report is based on the following settings:</p>", paddingLeft, 0, PDFGeneratorConstants.PageWitdh, RowHeight, FontFamily.Times, 9, false));

            var reportParameters = container.Rows.Add();

            var reportParametersTable = new Table(-5, 0, PDFGeneratorConstants.PageWitdh, 80);

            reportParametersTable.Border.LineStyle = LineStyle.None;
            reportParametersTable.CellDefault.Border.LineStyle = LineStyle.None;
            reportParametersTable.CellDefault.ParagraphSpacing = 0;

          

            reportParametersTable.Columns.Add(firstColumnWidth);
            reportParametersTable.Columns.Add(PDFGeneratorConstants.PageWitdh-firstColumnWidth);

            BuildReportParametersRow(reportParametersTable, "Weight Matrix:", _dataSource.ESGProfile.MatrixName, firstColumnWidth);
            BuildReportParametersRow(reportParametersTable, "Comparative group:", _dataSource.ESGProfile.ComparativeGroup == ComparativeGroupEnum.PeerGroup
                ? "Peer Group"
                : "Industry Group", firstColumnWidth);
            BuildReportParametersRow(reportParametersTable, "Reference Universe:", _dataSource.ESGProfile.ReferenceUniverseName, firstColumnWidth);
            
            BuildReportParametersRow(reportParametersTable, "Company type:", _dataSource.CorporateData.CompanyType, firstColumnWidth);
            BuildReportParametersRow(reportParametersTable, "Company type selection:", _dataSource.ESGProfile.CompanyType == CompanyTypeEnum.Combined
                ? _dataSource.ESGProfile.CompanyType.ToString()
                : "Company’s type", firstColumnWidth);

            BuildReportParametersRow(reportParametersTable, "Template type:", _dataSource.CorporateData.TemplateType, firstColumnWidth);
            BuildReportParametersRow(reportParametersTable, "Template type selection:", _dataSource.ESGProfile.TemplateType == TemplateTypeEnum.Combined
                ? _dataSource.ESGProfile.TemplateType.ToString()
                : "Company’s type", firstColumnWidth);

            reportParametersTable.Height = reportParametersTable.GetRequiredHeight();
            reportParameters.Cells.Add(reportParametersTable);



            var profileLogHeading = container.Rows.Add();

            AddSubsectionCell(profileLogHeading, "Profile Log", paddingLeft);

            var profileLogRow = container.Rows.Add();



            var profileLogTable = new Table(-5, 0, PDFGeneratorConstants.PageWitdh, 40);

            profileLogTable.Border.LineStyle = LineStyle.None;
            profileLogTable.CellDefault.Border.LineStyle = LineStyle.None;
            profileLogTable.CellDefault.ParagraphSpacing = 0;

           

            profileLogTable.Columns.Add(firstColumnWidth);
            profileLogTable.Columns.Add(PDFGeneratorConstants.PageWitdh - firstColumnWidth);

            if (_dataSource.CompanyProfile != null && _dataSource.CompanyProfile.LastAnnualReportDate != DateTime.MinValue)
                BuildReportParametersRow(profileLogTable, "Annual Report:", _dataSource.CompanyProfile.LastAnnualReportDate.Year.ToString(), firstColumnWidth);

            if (_dataSource.CompanyProfile != null && _dataSource.CompanyProfile.LastCSRDate != DateTime.MinValue)
                BuildReportParametersRow(profileLogTable, "CSR Report:", _dataSource.CompanyProfile.LastCSRDate.Year.ToString(), firstColumnWidth);

            if (_dataSource.CompanyProfile != null && _dataSource.CompanyProfile.LastFeedbackRequestDate != DateTime.MinValue)
                BuildReportParametersRow(profileLogTable, "Latest Feedback Request:", _dataSource.CompanyProfile.LastFeedbackRequestDate.ToString("MMM yyyy", new CultureInfo("en-US")), firstColumnWidth);

            if (_dataSource.CompanyProfile != null && _dataSource.CompanyProfile.LastFeedbackReceivedDate != DateTime.MinValue)
                BuildReportParametersRow(profileLogTable, "Company Response:", _dataSource.CompanyProfile.LastFeedbackReceivedDate.ToString("MMM yyyy", new CultureInfo("en-US")), firstColumnWidth);

            if (_dataSource.CompanyProfile != null)
            {
                var lastFullReviewDate = "N/A";

                if (_dataSource.CompanyProfile.LastFullReviewDate > DateTime.MinValue)
                {
                    lastFullReviewDate = _dataSource.CompanyProfile.LastFullReviewDate.ToString("MMM yyyy", new CultureInfo("en-US"));
                }

                BuildReportParametersRow(profileLogTable, "Last Analyst Review:", lastFullReviewDate, firstColumnWidth);
            }

            profileLogTable.Height=profileLogTable.GetRequiredHeight();
            profileLogRow.Cells.Add(profileLogTable);

            return container;


       
        }

        private static int RowHeight
        {
            get { return 8; }
        }

        private Table BuildSectionTable( string sectionName, float top=0)
        {
            var container = new Table(_report.LeftMargin, top, _report.PageMaxWidth, 300f);
            container.Border.LineStyle = LineStyle.None;
            container.CellDefault.Border.LineStyle = LineStyle.None;


            var headingRow = container.Rows.Add();

            container.Columns.Add(PDFGeneratorConstants.PageWitdh);

            var headingCell =
                headingRow.Cells.Add(new FormattedTextArea(String.Format(@"<p><font color='FFFFFF'><b>{0}</b></p>",sectionName), 0, 1f, PDFGeneratorConstants.PageWitdh, 18f,
                    Fonts.CalibriFontFamily, 16, false));
            headingCell.VAlign = VAlign.Center;
            headingCell.Align = TextAlign.Left;
            headingCell.BackgroundColor = Colors.SectionColor;
            
            return container;
        }

        private static void BuildReportParametersRow(Table table, string key, string value, float firstColumnWidth)
        {
            var row = table.Rows.Add();

            row.Cells.Add(new FormattedTextArea(key, 0, 0, firstColumnWidth, 6, Fonts.CalibriFontFamily, 9, false));

            if (string.IsNullOrEmpty(value)) return;

            row.Cells.Add(
                new FormattedTextArea(value, 0, 0, PDFGeneratorConstants.PageWitdh - firstColumnWidth, 6, 
                    Fonts.CalibriFontFamily, 9, false));
        }

        private static void AddSubsectionCell(Row firstSection, string sectionHeading, float paddingLeft)
        {

            firstSection.Height = 35;

            var c =
                 firstSection.Cells.Add(new Label(sectionHeading, paddingLeft, 0, PDFGeneratorConstants.PageWitdh, 13,
                     Fonts.CalibriBold, 13));
            c.VAlign = VAlign.Bottom;
            c.Align = TextAlign.Left;
            c.Border.LineStyle = LineStyle.Solid;
            c.Border.Width = 1.5f;
            c.Border.Top.LineStyle = LineStyle.None;
            c.Border.Left.LineStyle = LineStyle.None;
            c.Border.Right.LineStyle = LineStyle.None;

        }

        private static IEnumerable<Term> GetCompanyReportGlossary()
        {
            return new List<Term>
            {
                new Term
                {
                    Name = "Business Impact",
                    Definition = "The magnitude of the potential impact that a key ESG issue may have on the financial performance of companies within an industry."
                },
                new Term
                {
                    Name = "Controversy",
                    Definition = "An event or aggregation of events relating to an environmental, social and governance topic."
                },
                new Term
                {
                    Name = "Disclosure",
                    Definition = "A company’s transparency on its ESG preparedness and performance via sustainability reporting and its utilization of key reporting and verification standards."
                },
                new Term
                {
                    Name = "Event",
                    Definition = "A series of incidents which pertain to a common theme. An event assessment is based on the highest impact or risk score assigned to the related incidents, alongside a broader assessment of event trend and company preparedness and response."
                },
                new Term
                {
                    Name = "Historical ESG Performance",
                    Definition = "Historical scoring data over a rolling 36-month period. Note that the industry leader and industry average are based on Sustainalytics’ full research universe and default weight matrix rather than customized portfolio settings or weights."
                },
                new Term
                {
                    Name = "Incident",
                    Definition = "Company operations and/or products and services that have a negative impact on the environment, society and external stakeholders."
                },
                new Term
                {
                    Name = "Indicator Performance",
                    Definition = "How a company performs on the indicators on which it has been assessed."
                },
                new Term
                {
                    Name = "Leader",
                    Definition = "The score of the best performing company in the industry or any selected subset of companies."
                },
                new Term
                {
                    Name = "Key ESG issue",
                    Definition = "Key ESG issues identify the most important areas in which a company has potential to cause significant sustainability impacts or experience significant ESG-related business impacts, and that must therefore be managed effectively. To determine the most relevant ESG issues per industry, lead analysts assessed potential of each industry to cause significant sustainability impacts and, separately, the potential for these to impact corporate financial performance."
                },
                new Term
                {
                    Name = "Leading Practice",
                    Definition = "<p SpacingAfter=5>Leading Practice Average: For each indicator, the Leading Practice Average is the top quartile mean score among industry peers.</p><p>Leading Practice Gap: The Leading Practice Gap is the difference between the Leading Practice Average and the company’s score for that particular indicator. The size of the gap is indicated by a colored dot.</p>"
                },
                new Term
                {
                    Name = "Outlook",
                    Definition = "A forecast of how a controversy rating will change over the next 12 months."
                },
                new Term
                {
                    Name = "Overall ESG Score",
                    Definition = "Assessment of a company’s overall ESG preparedness and performance. These scores are dynamic, depending on the weight matrix, comparison group and reference universe selected."
                },
                new Term
                {
                    Name = "Percentile",
                    Definition = "The company’s percentile rank within its industry or within another client-selected reference group."
                },
                new Term
                {
                    Name = "Preparedness",
                    Definition = "A company’s systems and policies to manage potential ESG impacts and risks. Key indicators of a company’s ESG preparedness include policies, management systems, programs and targets."
                },
                new Term
                {
                    Name = "Qualitative Performance",
                    Definition = "A company’s ESG performance based on qualitative information relating to events and incidents that have resulted in negative ESG impacts."
                },
                new Term
                {
                    Name = "Quantitative Performance",
                    Definition = "A company’s ESG performance based on quantitative social and environmental metrics such as carbon intensity, number of fatalities, etc."
                },
                new Term
                {
                    Name = "Relative Position",
                    Definition = "The company's performance classification is relative to its global industry peers, based on the company's absolute ESG score. Each industry has a fixed band of scores that links to a relative position range. Companies can fall in five categories: laggards, underperformers, average performers, outperformers and leaders. The industry specific bands are based on the rating scores of approximately 4,000 companies that are assessed under the Sustainalytics comprehensive ESG Ratings framework. The bands are reviewed and updated annually."
                },
                new Term
                {
                    Name = "Sustainability Impact",
                    Definition = "The magnitude of potential sustainability impacts (measured in terms of depth, breadth, and duration) that may be caused by an industry’s activities if not managed effectively."
                },
                new Term
                {
                    Name = "Tag",
                    Definition = "A key word associated with a key ESG issue and linked to incidences to facilitate search functionalities."
                }
            };

        }

        private static IEnumerable<Term> GetFocusedRatingsGlossary()
        {
            return new List<Term>
            {
                new Term
                {
                    Name = "Business Impact",
                    Definition = "The magnitude of the potential impact that a key ESG issue may have on the financial performance of companies within an industry."
                },
                new Term
                {
                    Name = "Controversy",
                    Definition = "An event or aggregation of events relating to an environmental, social and governance topic."
                },
                new Term
                {
                    Name = "Disclosure",
                    Definition = "A company’s transparency on its ESG preparedness and performance via sustainability reporting and its utilization of key reporting and verification standards."
                },
                new Term
                {
                    Name = "Event",
                    Definition = "A series of incidents which pertain to a common theme. An event assessment is based on the highest impact or risk score assigned to the related incidents, alongside a broader assessment of event trend and company preparedness and response."
                },
                new Term
                {
                    Name = "Historical ESG Performance",
                    Definition = "Historical scoring data over a rolling 36-month period. Note that the industry leader and industry average are based on the universe of approximately 4,000 companies that are assessed under the Sustainalytics comprehensive ESG Ratings framework."
                },
                new Term
                {
                    Name = "Incident",
                    Definition = "Company operations and/or products and services that have a negative impact on the environment, society and external stakeholders."
                },
                new Term
                {
                    Name = "Leader",
                    Definition = "The score of the best performing company in the industry, based on the universe of approximately 4,000 companies that are assessed under the Sustainalytics comprehensive ESG Ratings framework."
                },
                new Term
                {
                    Name = "Leading Practice",
                    Definition = "<p>Leading Practice Average: For each indicator, the Leading Practice Average is the top quartile mean score among industry peers, based on the universe of approximately 4,000 companies that are assessed under the Sustainalytics comprehensive ESG Ratings framework.</p><p>Leading Practice Gap: The Leading Practice Gap is the difference between the Leading Practice Average and the company’s score for that particular indicator. The size of the gap is indicated by a colored dot.</p>"
                },
                new Term
                {
                    Name = "Outlook",
                    Definition = "A forecast of how a controversy rating will change over the next 12 months."
                },
                new Term
                {
                    Name = "Overall ESG Score",
                    Definition = "Assessment of a company’s overall ESG preparedness and performance."
                },
                new Term
                {
                    Name = "Percentile",
                    Definition = "The company’s percentile is an assigned value indicating the company’s percentile rank relative to the universe of approximately 4,000 companies that are assessed under the Sustainalytics comprehensive ESG Ratings framework."
                },
                new Term
                {
                    Name = "Relative Position",
                    Definition = "The company's performance classification is relative to its global industry peers, based on the company's absolute ESG score. Each industry has a fixed band of scores that links to a relative position range. Companies can fall in five categories: laggards, underperformers, average performers, outperformers and leaders. The industry specific bands are based on the rating scores of approximately 4,000 companies that are assessed under the Sustainalytics comprehensive ESG Ratings framework. The bands are reviewed and updated annually."
                },
                new Term
                {
                    Name = "Sustainability Impact",
                    Definition = "The magnitude of potential sustainability impacts (measured in terms of depth, breadth, and duration) that may be caused by an industry’s activities if not managed effectively."
                },
            };

        }

        private static IEnumerable<Term> GetControversyReportGlossary()
        {
            return new List<Term>
            {
                new Term
                {
                    Name = "Incident",
                    Definition = "Company operations and/or products and services that have a negative impact on the environment, society and external stakeholders."
                },
                new Term
                {
                    Name = "Event",
                    Definition = "A series of incidents which pertain to a common theme. An event assessment is based on the highest impact or risk score assigned to the related incidents, alongside a broader assessment of event trend and company preparedness and response."
                },
                new Term
                {
                    Name = "Outlook",
                    Definition = "A forecast of how a controversy rating will change over the next 12 months."
                },
                new Term
                {
                    Name = "Controversy",
                    Definition = "An event or aggregation of events relating to an environmental, social and governance topic."
                }
            };
        }


        private static float GetRequiredHeightOfTermDefinition(Term term)
        {
            const float lineBreak = 18f;

            var requiredHeight = lineBreak;

            var formattedTextArea = new FormattedTextArea(string.Format("<b>{0}</b>", term.Definition), 0, 0 + 5f, 545, 100, Fonts.CalibriFontFamily, 9, false);
            requiredHeight += (float)Math.Round((decimal)formattedTextArea.GetRequiredHeight(), 0, MidpointRounding.AwayFromZero);

            requiredHeight += lineBreak;

            if (term.Name.Contains("Indicator Performance"))
            {
                requiredHeight += 100f;
            }
            if (term.Name.Contains("Outlook"))
            {
                requiredHeight += 80f;
            }
            if (term.Name.Contains("Qualitative Performance"))
            {
                requiredHeight += 120f;
            }
            if (term.Name.Contains("Relative Position"))
            {
                requiredHeight += 95f;
            }
            if (term.Name.Contains("Event"))
            {
                requiredHeight += 115f;
            }

            return requiredHeight;
        }

        private float AddGlossaryOfTermsForCompanyReport(PdfReportPage page, float top)
        {
            const float lineBreak = 18f;
            
            var titleTable = BuildSectionTable("Glossary of Terms", top);

            page.AddElement(titleTable);

            top += titleTable.GetRequiredHeight()+20;

            var remainingHeight = _report.PageMaxHeight - top;

            var glossary = GetCompanyReportGlossary();

            foreach (var term in glossary.OrderBy(g => g.Name))
            {
                
                
                var requiredHeight = GetRequiredHeightOfTermDefinition(term);

                if (remainingHeight < requiredHeight)
                {
                    page = _report.NewPage();

                    _report.Pages.Add(page);
                    top = 0;
                    remainingHeight = _report.PageMaxHeight;
                }

                AddTerm(term.Name, page, _report.LeftMargin, top);
                top += lineBreak;
                remainingHeight -= lineBreak;

                var descriptionHeight = AddTermDescription(term.Definition, page, _report.LeftMargin, top, requiredHeight);
                top += descriptionHeight + lineBreak;
                remainingHeight -= descriptionHeight + lineBreak;

                if (term.Name.Contains("Indicator Performance"))
                {
                    AddIndicatorPerformanceDefinition(page, _report.LeftMargin, top);
                    top += 100f;
                    remainingHeight -= 100f;
                }
                if (term.Name == "Key ESG issue")
                {
                    var height = AddKeyEsgIssuesDefinition(page, _report.LeftMargin, top);

                    top += height;
                    remainingHeight -= height;
                }
                if (term.Name.Contains("Outlook"))
                {
                    AddOutlookDefinition(page, _report.LeftMargin, top);
                    top += 80f;
                    remainingHeight -= 80f;
                }
                if (term.Name.Contains("Qualitative Performance"))
                {
                    AddQualitativePerformanceDefinition(page, _report.LeftMargin, top);
                    top += 120f;
                    remainingHeight -= 120f;
                }
                if (term.Name.Contains("Event"))
                {
                    AddEventDefinition(page, _report.LeftMargin, top);
                    top += 115f;
                    remainingHeight -= 115f;
                }
            }

            return top;
        }

        private float AddGlossaryOfTermsForFocusedRatings(PdfReportPage page, float top)
        {
            const float lineBreak = 18f;

            var titleTable = BuildSectionTable("Glossary of Terms", top);

            page.AddElement(titleTable);

            top += titleTable.GetRequiredHeight() + 20;

            var remainingHeight = _report.PageMaxHeight - top;

            var glossary = GetFocusedRatingsGlossary();

            foreach (var term in glossary.OrderBy(g => g.Name))
            {               
                var requiredHeight = GetRequiredHeightOfTermDefinition(term);

                if (remainingHeight < requiredHeight)
                {
                    page = _report.NewPage();

                    _report.Pages.Add(page);
                    top = 0;
                    remainingHeight = _report.PageMaxHeight;
                }

                AddTerm(term.Name, page, _report.LeftMargin, top);
                top += lineBreak;
                remainingHeight -= lineBreak;

                var descriptionHeight = AddTermDescription(term.Definition, page, _report.LeftMargin, top, requiredHeight);
                top += descriptionHeight + lineBreak;
                remainingHeight -= descriptionHeight + lineBreak;

                if (term.Name.Contains("Outlook"))
                {
                    AddOutlookDefinition(page, _report.LeftMargin, top);
                    top += 80f;
                    remainingHeight -= 80f;
                }

                if (term.Name.Contains("Event"))
                {
                    AddEventDefinition(page, _report.LeftMargin, top);
                    top += 115f;
                    remainingHeight -= 115f;
                }
            }

            return top;
        }

        private float AddGlossaryOfTermsForControversyReport(PdfReportPage page, float top)
        {
            const float lineBreak  = 15f;
            
            var titleTable = BuildSectionTable("Glossary of Terms", top);

            page.AddElement(titleTable);

            top += titleTable.GetRequiredHeight() + 20;

            var remainingHeight = _report.PageMaxHeight - top;


            var glossary = GetControversyReportGlossary();

            foreach (var term in glossary.OrderBy(g => g.Name))
            {
                var requiredHeight = GetRequiredHeightOfTermDefinition(term);

                if (remainingHeight < requiredHeight)
                {
                    page = _report.NewPage();

                    _report.Pages.Add(page);
                    top = 0;
                    remainingHeight = _report.PageMaxHeight;
                }

                AddTerm(term.Name, page, _report.LeftMargin, top);
                top += lineBreak;
                remainingHeight -= lineBreak;

                var descriptionHeight = AddTermDescription(term.Definition, page, _report.LeftMargin, top, requiredHeight);
                top += descriptionHeight + lineBreak;
                remainingHeight -= descriptionHeight + lineBreak;

                if (term.Name.Contains("Event"))
                {
                    AddEventDefinition(page, _report.LeftMargin, top);
                    top += 115f;
                    remainingHeight -= 115f;
                }
                if (term.Name.Contains("Outlook"))
                {
                    AddOutlookDefinition(page, _report.LeftMargin, top);
                    top += 80f;
                    remainingHeight -= 80f;
                }
            }

            return top;
        }

        private static void AddTerm(string term, PdfReportPage page, float x, float top)
        {
            page.Elements.Add(new FormattedTextArea(string.Format("<p><b>{0}</b></p>", term), x, top, 215, 12, Fonts.CalibriFontFamily, 12, false));
            page.Elements.Add(new Line(x, top + 15, 545, top + 15, 1f, RgbColor.Black));
        }

        private static float AddTermDescription(string text, PdfReportPage page, float x, float top, float textHeight)
        {
            var formattedTextArea = new FormattedTextArea(string.Format("<p>{0}</p>", text), x, top + 5f, 545, textHeight, Fonts.CalibriFontFamily, 9, false);
            page.Elements.Add(formattedTextArea);
            var requiredHeight = (float)Math.Round((decimal)formattedTextArea.GetRequiredHeight(), 0, MidpointRounding.AwayFromZero);
            return requiredHeight;
        }

        private float AddKeyEsgIssuesDefinition(PdfReportPage page, float x, float top)
        {         
            var table = new Table(x, top, 545, 135);
            table.Border.Width = 0f;
            table.CellSpacing = 0.0f;
            table.CellDefault.Align = TextAlign.Left;
            table.CellDefault.VAlign = VAlign.Top;
            table.CellDefault.Padding.Value = 0.0f;
            table.CellDefault.Border.Color = RgbColor.Blue;
            table.CellDefault.Border.Width = 0f;
            table.CellDefault.Border.LineStyle = LineStyle.None;
            table.Border.Color = Grayscale.Black;
            table.RepeatColumnHeaderCount = 0;
            table.RepeatRowHeaderCount = 0;
            table.Columns.Add(180);
            table.Columns.Add(365);

            foreach (var kei in _dataSource.KeyEsgIssues)
            {
                var row = table.Rows.Add(10);
                row.CellDefault.Padding.Bottom = 5f;
                row.Cells.Add(kei.Name, Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
                var cell = row.Cells.Add(kei.Description, Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
                cell.Align = TextAlign.Left;
            }

            var height = table.GetRequiredHeight() + 5f;
            table.Height = height;

            page.Elements.Add(table);

            return height;
        }

        private void AddIndicatorPerformanceDefinition(PdfReportPage page, float x, float top)
        {
            var table = new Table(x, top, 545, 170, Fonts.CalibriRegular, 9);
            table.Border.Width = 0f;
            table.CellSpacing = 0.0f;
            table.CellDefault.Align = TextAlign.Left;
            table.CellDefault.VAlign = VAlign.Top;
            table.CellDefault.Padding.Value = 0.0f;
            table.CellDefault.Border.Color = RgbColor.Blue;
            table.CellDefault.Border.Width = 0f;
            table.CellDefault.Border.LineStyle = LineStyle.None;
            table.Border.Color = Grayscale.Black;
            table.RepeatColumnHeaderCount = 0;
            table.RepeatRowHeaderCount = 0;
            table.Columns.Add(180);
            table.Columns.Add(365);
            var row1 = table.Rows.Add(10);
            row1.Cells.Add("Key Indicator", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell11 = row1.Cells.Add("Indicators that relate to an industry key ESG issue, provide considerable insight into a company’s ESG preparedness/performance, and/or distinguish between leaders and laggards.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell11.Align = TextAlign.Left;
            var row2 = table.Rows.Add(10);
            row2.Cells.Add("Raw Score", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell21 = row2.Cells.Add("A score out of 100 assigned to an indicator based on a set of internal criteria.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell21.Align = TextAlign.Left;
            var row3 = table.Rows.Add(10);
            row3.Cells.Add("Indicator Weight", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell31 = row3.Cells.Add("The percentage of weight assigned to each indicator in an industry.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell31.Align = TextAlign.Left;
            var row4 = table.Rows.Add(10);
            row4.Cells.Add(string.Empty, Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell41 = row4.Cells.Add("Note: weight matrices are customizable.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell41.Align = TextAlign.Left;
            var row5 = table.Rows.Add(10);
            row5.Cells.Add("Weighted score", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell51 = row5.Cells.Add("The raw score multiplied by the weight assigned per indicator.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell51.Align = TextAlign.Left;
            var row6 = table.Rows.Add(10);
            row6.Cells.Add("Trend Icons", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell61 = row6.Cells.Add("Indicates if the raw score of an indicator improved, declined or remained unchanged over the last 12 months.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell61.Align = TextAlign.Left;
            page.Elements.Add(table);

            top += 77;

            page.Elements.Add(new Image(ImageData.GetImage(TrendArrowUp), x, top, 0.08f));
            page.Elements.Add(new Image(ImageData.GetImage(TrendArrowDown), x + 15, top, 0.08f));
            page.Elements.Add(new Image(ImageData.GetImage(TrendArrowRight), x + 30, top, 0.08f));
        }

        private static void AddOutlookDefinition(PdfReportPage page, float x, float top)
        {
            var table = new Table(x, top, 545, 135, Fonts.CalibriRegular, 9);
            table.Border.Width = 0f;
            table.CellSpacing = 0.0f;
            table.CellDefault.Align = TextAlign.Left;
            table.CellDefault.VAlign = VAlign.Top;
            table.CellDefault.Padding.Value = 0.0f;
            table.CellDefault.Border.Color = RgbColor.Blue;
            table.CellDefault.Border.Width = 0f;
            table.CellDefault.Border.LineStyle = LineStyle.None;
            table.Border.Color = Grayscale.Black;
            table.RepeatColumnHeaderCount = 0;
            table.RepeatRowHeaderCount = 0;
            table.Columns.Add(180);
            table.Columns.Add(365);
            var row1 = table.Rows.Add(10);
            row1.Cells.Add("Negative", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell11 = row1.Cells.Add("The event is likely to deteriorate within 12 months, leading to a downgraded rating of the corresponding indicator.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell11.Align = TextAlign.Left;
            var row2 = table.Rows.Add(10);
            row2.Cells.Add("Positive", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell21 = row2.Cells.Add("The event is likely to improve within the next 12 months, leading to an upgraded rating of the corresponding indicator.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell21.Align = TextAlign.Left;
            var row3 = table.Rows.Add(20);
            row3.Cells.Add("Neutral", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell31 = row3.Cells.Add("The event is unlikely to change significantly within the next 12 months, and is not expected to undergo a change in rating of the corresponding indicator.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell31.Align = TextAlign.Left;
            page.Elements.Add(table);
        }

        private static void AddQualitativePerformanceDefinition(PdfReportPage page, float x, float top)
        {
            var table = new Table(x, top, 545, 290, Fonts.CalibriRegular, 10);
            table.Border.Width = 0f;
            table.CellSpacing = 0.0f;
            table.CellDefault.Align = TextAlign.Left;
            table.CellDefault.VAlign = VAlign.Top;
            table.CellDefault.Padding.Value = 0.0f;
            table.CellDefault.Border.Color = RgbColor.Blue;
            table.CellDefault.Border.Width = 0f;
            table.CellDefault.Border.LineStyle = LineStyle.None;
            table.Border.Color = Grayscale.Black;
            table.RepeatColumnHeaderCount = 0;
            table.RepeatRowHeaderCount = 0;
            table.Columns.Add(180);
            table.Columns.Add(360);
            var row1 = table.Rows.Add(20);
            row1.Cells.Add("Category 5 – Severe (raw score of 0):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell11 = row1.Cells.Add("The event has a severe impact on the environment and society, posing serious risks to the company. This category represents the most egregious corporate behavior.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell11.Align = TextAlign.Left;
            var row2 = table.Rows.Add(20);
            row2.Cells.Add("Category 4 – High (raw score of 20):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell21 = row2.Cells.Add("The event has a high impact on the environment and society, posing significant risks to the company. This category often reflects structural problems in the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell21.Align = TextAlign.Left;
            var row3 = table.Rows.Add(20);
            row3.Cells.Add("Category 3 – Significant (raw score of 50):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell31 = row3.Cells.Add("The event has a significant impact on the environment and society, posing moderate risks to the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell31.Align = TextAlign.Left;
            var row4 = table.Rows.Add(20);
            row4.Cells.Add("Category 2 – Moderate (raw score of 80):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell41 = row4.Cells.Add("The event has a moderate impact on the environment and society, posing minimal risks to the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell41.Align = TextAlign.Left;
            var row5 = table.Rows.Add(20);
            row5.Cells.Add("Category 1 – Low (raw score of 99):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell51 = row5.Cells.Add("The event has a low impact on the environment and society, posing negligible risks to the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell51.Align = TextAlign.Left;
            page.Elements.Add(table);
        }

        private static void AddEventDefinition(PdfReportPage page, float x, float top)
        {
            var table = new Table(x, top, 545, 290, Fonts.CalibriRegular, 10);
            table.Border.Width = 0f;
            table.CellSpacing = 0.0f;
            table.CellDefault.Align = TextAlign.Left;
            table.CellDefault.VAlign = VAlign.Top;
            table.CellDefault.Padding.Value = 0.0f;
            table.CellDefault.Border.Color = RgbColor.Blue;
            table.CellDefault.Border.Width = 0f;
            table.CellDefault.Border.LineStyle = LineStyle.None;
            table.Border.Color = Grayscale.Black;
            table.RepeatColumnHeaderCount = 0;
            table.RepeatRowHeaderCount = 0;
            table.Columns.Add(180);
            table.Columns.Add(360);
            var row1 = table.Rows.Add(20);
            row1.Cells.Add("Category 5 – Severe (raw score of 0):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell11 = row1.Cells.Add("The event has a severe impact on the environment and society, posing serious risks to the company. This category represents the most egregious corporate behavior.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell11.Align = TextAlign.Left;
            var row2 = table.Rows.Add(20);
            row2.Cells.Add("Category 4 – High (raw score of 20):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell21 = row2.Cells.Add("The event has a high impact on the environment and society, posing significant risks to the company. This category often reflects structural problems in the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell21.Align = TextAlign.Left;
            var row3 = table.Rows.Add(20);
            row3.Cells.Add("Category 3 – Significant (raw score of 50):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell31 = row3.Cells.Add("The event has a significant impact on the environment and society, posing moderate risks to the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell31.Align = TextAlign.Left;
            var row4 = table.Rows.Add(20);
            row4.Cells.Add("Category 2 – Moderate (raw score of 80):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell41 = row4.Cells.Add("The event has a moderate impact on the environment and society, posing minimal risks to the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell41.Align = TextAlign.Left;
            var row5 = table.Rows.Add(20);
            row5.Cells.Add("Category 1 – Low (raw score of 99):", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            var cell51 = row5.Cells.Add("The event has a low impact on the environment and society, posing negligible risks to the company.", Fonts.CalibriRegular, 9, RgbColor.Black, RgbColor.White, 1);
            cell51.Align = TextAlign.Left;
            page.Elements.Add(table);
        }

        private void AddDisclaimer(PdfReportPage page, float top)
        {
            var width = _report.PageMaxWidth;
            var height = _report.PageMaxHeight - 25f;

            const string title = "Disclaimer";
            const float fontSize = 9;
            var style = new FormattedTextAreaStyle(Fonts.CalibriLightFontFamily, 9f, false)
            {
                Paragraph = { Align = TextAlign.Justify, Indent = 0, SpacingAfter = 0, AllowOrphanLines = false }
            };

            var copyright1 = $"Copyright ©{DateTime.UtcNow.Year} Sustainalytics. All rights reserved.";

            var table = new Table(_report.LeftMargin, top, width, height);
            table.Border.Width = 0.0f;
            table.Border.Color = Grayscale.Black;
            table.Border.LineStyle = LineStyle.None;
            table.BackgroundColor = RgbColor.White;
            table.CellSpacing = 0.0f;
            table.CellDefault.Align = TextAlign.Left;
            table.CellDefault.VAlign = VAlign.Top;
            table.CellDefault.Padding.Value = 0.0f;
            table.CellDefault.Border.Width = 0.0f;
            table.CellDefault.Border.LineStyle = LineStyle.None;
            table.CellDefault.Border.Color = RgbColor.White;

            table.Columns.Add(width);

            var row = table.Rows.Add(12f);
            var cell = row.Cells.Add(new Label(title, 0.0f, 0.0f, width,
                    12f, Fonts.CalibriBold, 12f));
            cell.Border.Bottom.LineStyle = LineStyle.Solid;
            cell.Border.Bottom.Width = 1f;
            cell.Border.Bottom.Color = RgbColor.Black;

            row = table.Rows.Add(fontSize);
            row.Cells.Add("");

            row = table.Rows.Add(fontSize);
            row.Cells.Add(new Label(copyright1, 0.0f, 0.0f, width,
                    fontSize + 2f, Fonts.CalibriBold, fontSize));


            var disclaimer = "<p>The ownership and all intellectual property rights to this publication/report and the information contained herein are vested exclusively in Sustainalytics and/or its suppliers. Unless otherwise expressly agreed in writing between you and Sustainalytics, you will not be permitted to use this information otherwise than for internal use, nor will you be permitted to reproduce, disseminate, comingle, create derivative works, furnish in any manner, make available to third parties or publish this publication/report, parts hereof or the information contained herein in any form or in any manner, be it electronically, mechanically, through photocopies, recordings.  The information on which this publication/report is based on reflects the situation as on the date of its elaboration. Such information has – fully or partially – been derived from third parties and is therefore subject to continuous modification. " +
                "THE INFORMATION HEREIN IS PROVIDED SOLELY FOR INFORMATIONAL PURPOSES AND THEREFORE ARE NOT AN OFFER TO BUY OR SELL A SECURITY. NEITHER SUSTAINALYTICS NOR ALL ITS THIRD-PARTY SUPPLIERS PROVIDE INVESTMENT ADVICE (AS DEFINED IN THE APPLICABLE JURISDICTION) OR ANY OTHER FORM OF (FINANCIAL) ADVICE AND NOTHING WITHIN THIS PUBLICATION/REPORT CONSTITUTES SUCH ADVICE. SUSTAINALYTICS OBSERVES THE GREATEST POSSIBLE CARE IN USING INFORMATION, HOWEVER THE INFORMATION IS PROVIDED “AS IS” AND NEITHER SUSTAINALYTICS NOR ITS SUPPLIERS ACCEPT ANY LIABILITY FOR DAMAGE ARISING FROM THE USE OF THIS PUBLICATION/REPORT OR INFORMATION CONTAINED HEREIN IN ANY MANNER WHATSOEVER. MOREOVER, SUSTAINALYTICS AND ALL ITS THIRD-PARTY SUPPLIERS DISCLAIM ANY AND ALL WARRANTIES AND REPRESENTATIONS, EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OF MERCHANTIBILITY, COMPLETENESS, ACCURACY OR FITNESS FOR A PARTICULAR PURPOSE.</p>";

            var formattedTextArea = new FormattedTextArea(disclaimer, 0.0f, 0.0f, width, 500f, style);
            var textAreaHeight = formattedTextArea.GetRequiredHeight() + 5f;
            formattedTextArea = new FormattedTextArea(disclaimer, 0.0f, 0.0f, width, textAreaHeight, style);

            row = table.Rows.Add(textAreaHeight);
            row.Cells.Add(formattedTextArea);

            row = table.Rows.Add(fontSize);
            row.Cells.Add("");



            disclaimer = "<p>This publication/report may contain proprietary information from third parties (Third Party Data) and here you can find additional terms and conditions imposed by the following Third Party Data providers regarding the use of their data:</ p>";
            formattedTextArea = new FormattedTextArea(disclaimer, 0.0f, 0.0f, width, 500f, style);
            textAreaHeight = formattedTextArea.GetRequiredHeight() + 5f;
            formattedTextArea = new FormattedTextArea(disclaimer, 0.0f, 0.0f, width, textAreaHeight, style);

            row = table.Rows.Add(textAreaHeight);
            row.Cells.Add(formattedTextArea);

            row = table.Rows.Add(fontSize);
            row.Cells.Add("");


            const string text = "www.sustainalytics.com/legal-disclaimers";
            var font = Fonts.CalibriBold;

            // Create the link's label
            var label = new Label(text, 0.0f, 0.0f, width, 10f, font, 9f, new RgbColor(240, 171, 0));

            // Set the action then create the link
            var action = new UrlAction("www.sustainalytics.com/legal-disclaimers");
            var link = new Link(0, 0, font.GetTextWidth(text, 9), 20, action);

            // Add the label and the link to the page
            var elements = new Group();
            elements.Add(label);
            elements.Add(link);

            row = table.Rows.Add(fontSize);
            row.Cells.Add(elements);

            page.Elements.Add(table);
        }
    }
}
